﻿using Furion.CMS.Core;
using Furion.DatabaseAccessor;
using Furion.EventBus;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace Furion.CMS.EntityFramework.Core
{
    [AppDbContext("MySqlConnectionString", DbProvider.MySql)]
    public class CoreDbContext : AppDbContext<CoreDbContext>
    {
        public CoreDbContext(DbContextOptions<CoreDbContext> options)
            : base(options)
        {
            EnabledEntityChangedListener = true; // 开启数据变更监听
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            // 将默认迁移 sql 生成器服务替换为更新的服务
            optionsBuilder.ReplaceService<IMigrationsSqlGenerator, ExtendedSqlGenerator>();
        }

        /// <summary>
        /// 重写保存之前事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void SavingChangesEvent(DbContextEventData eventData, InterceptionResult<int> result)
        {
            // 获取当前事件对应上下文
            var dbContext = eventData.Context;
            var _eventPublisher = App.GetRequiredService<IEventPublisher>();

            // 强制重新检查一边实体更改信息
            // dbContext.ChangeTracker.DetectChanges();

            // 获取所有更改，删除，新增的实体且包含了OperateRecordAttribute头信息的实体
            var entities = dbContext.ChangeTracker.Entries()
                .Where(u => (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added))
                .ToList();

            // 通过请求中获取当前操作人
            var httpContext = App.GetService<IHttpContextAccessor>().HttpContext;
            var userId = httpContext?.User.FindFirstValue(AppClaimConsts.UserId);
            string remoteIp = httpContext?.GetRemoteIpAddressToIPv4();

            // 获取所有已更改的实体
            foreach (var entity in entities)
            {
                // 获取实体类型
                var entityType = entity.Entity.GetType();
                // 获取实体的操作记录配置
                var operateRecordSetting = entityType.GetCustomAttributes(typeof(OperateRecordAttribute), true)
                    .Cast<OperateRecordAttribute>()
                    .FirstOrDefault();
                if (operateRecordSetting == null) continue;

                // 获取实体的元数据
                var metadataType = entityType.GetCustomAttributes(typeof(MetadataTypeAttribute), true)
                    .Cast<MetadataTypeAttribute>()
                    .FirstOrDefault();

                // 获取所有实体有效属性，排除 [NotMapper] 属性
                var props = entity.OriginalValues.Properties;

                // 获取实体当前（现在）的值
                var currentValues = entity.CurrentValues;

                // 获取数据库中实体的值
                var databaseValues = entity.GetDatabaseValues();

                // 数据审计实体
                var auditEvent = new DbAuditEventSource(entity.State.ToString(), userId, remoteIp, operateRecordSetting.DisplayName, entityType.Name);
                var changeList = new List<DbAuditContent>();

                // 遍历所有属性
                foreach (var prop in props)
                {
                    // 获取属性名
                    var propName = prop.Name;
                    // 获取操作记录配置 从实体或附加元数据中获取
                    var recordSetting = prop.PropertyInfo?.GetCustomAttributes(typeof(OperateRecordAttribute), true)
                        .Cast<OperateRecordAttribute>()
                        .FirstOrDefault() ?? metadataType?.MetadataClassType.GetProperty(propName)?
                            .GetCustomAttributes(typeof(OperateRecordAttribute), true)
                            .Cast<OperateRecordAttribute>()
                            .FirstOrDefault();

                    if (recordSetting == null) continue;

                    if (recordSetting.HasTagField)
                    {
                        var valueObj = databaseValues?[propName] ?? currentValues?[propName];
                        auditEvent.RowKey = recordSetting.TransitionHandle == null ? valueObj : (databaseValues ?? currentValues);
                        auditEvent.TransitionHandle = recordSetting.TransitionHandle;
                    }

                    // 获取现在的实体值
                    var newValue = currentValues[propName];

                    object oldValue = null;
                    // 如果是新增数据，则 databaseValues 为空，所以需要判断一下
                    if (databaseValues != null)
                    {
                        oldValue = databaseValues[propName];
                    }
                    // 变更记录
                    if (newValue?.Equals(oldValue) == false)
                    {
                        changeList.Add(new DbAuditContent(propName, recordSetting.DisplayName, oldValue, newValue,
                            recordSetting.TransitionHandle));
                    }
                }

                if (changeList.Any() || entity.State == EntityState.Deleted)
                {
                    auditEvent.Payload = changeList;
                    // 异步发送事件
                    _eventPublisher.PublishAsync(auditEvent);
                }
            }
        }
    }
}
